
/*
* Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
*
* NVIDIA Corporation and its licensors retain all intellectual property and proprietary
* rights in and to this software, related documentation and any modifications thereto.
* Any use, reproduction, disclosure or distribution of this software and related
* documentation without an express license agreement from NVIDIA Corporation is strictly
* prohibited.
*
* TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS*
* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE.  IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY
* SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT
* LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF
* BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGES
*/

#include <optix.h>
#include <optixu/optixu_math_namespace.h>
#include <optixu/optixu_matrix_namespace.h>
#include <optixu/optixu_aabb_namespace.h>

using namespace optix;

// This is to be plugged into an RTgeometry object to represent
// a triangle mesh with a vertex buffer of triangle soup (triangle list)
// with an interleaved position, normal, texturecoordinate layout.

rtBuffer<float3> vertex_buffer;     
rtBuffer<float3> normal_buffer;
rtBuffer<float2> texcoord_buffer;
rtBuffer<int3>   vindex_buffer;    // position indices 
rtBuffer<int3>   nindex_buffer;    // normal indices
rtBuffer<int3>   tindex_buffer;    // texcoord indices
rtBuffer<int>   tagindex_buffer;	// triangle indices
rtDeclareVariable(float, min_fovy, , );
rtDeclareVariable(float, max_radius2, , );
rtDeclareVariable(float,         rtpass_default_radius2, , );
rtDeclareVariable(float,         eye_lose_angle, , );
rtDeclareVariable(uint, need_unsurface, , );


rtBuffer<uint>   material_buffer; // per-face material index
rtDeclareVariable(float3, texcoord, attribute texcoord, ); 
rtDeclareVariable(float3, geometric_normal, attribute geometric_normal, ); 
rtDeclareVariable(int4, triangle_info, attribute triangle_info, ); 
rtDeclareVariable(int, is_soft_surface, attribute is_soft_surface, ); 
rtDeclareVariable(float3, shading_normal, attribute shading_normal, ); 
rtDeclareVariable(optix::Ray, ray, rtCurrentRay, );
rtDeclareVariable(float, triangle_area, attribute triangle_area, ); 

RT_PROGRAM void mesh_intersect( int primIdx )
{
	if (tagindex_buffer[primIdx] < 0)
		return;
	int3 v_idx = vindex_buffer[primIdx];

	float3 p0 = vertex_buffer[ v_idx.x ];
	float3 p1 = vertex_buffer[ v_idx.y ];
	float3 p2 = vertex_buffer[ v_idx.z ];

	// Intersect ray with triangle
	float3 n;
	float  t, beta, gamma;
	if( intersect_triangle( ray, p0, p1, p2, n, t, beta, gamma ) ) {

		if(  rtPotentialIntersection( t ) ) {
			float3 e01 = p1 - p0, e02 = p2 - p0, e12 = p2 - p1;
			float small_height = length(cross(e01, e02))/length(e02);
			float height1 = length(cross(e01, e02))/length(e01);
			if (small_height > height1)
				small_height = height1;
			float height2 = length(cross(e01, e12))/length(e01);
			if (small_height > height2)
				small_height = height2;

			triangle_info = make_int4(v_idx, tagindex_buffer[primIdx]);
			int3 n_idx = nindex_buffer[ primIdx ];

			/*shading_normal = normalize( n );*/
			geometric_normal = normalize( n );
			is_soft_surface = 0;

			if ( n_idx.x < 0 || n_idx.y < 0 || n_idx.z < 0 ) {
				shading_normal = normalize( n );
				is_soft_surface = 0;
				//return;
			} else 
			{
				float3 n0 = normal_buffer[ n_idx.x ];
				float3 n1 = normal_buffer[ n_idx.y ];
				float3 n2 = normal_buffer[ n_idx.z ];
				shading_normal = normalize( n1*beta + n2*gamma + n0*(1.0f-beta-gamma) );
// 				if (dot(geometric_normal, n0) < 0.999999 || dot(geometric_normal, n1) < 0.99999 ||
// 					dot(geometric_normal, n2) < 0.999999)
				if (dot(geometric_normal, n0) < 0.999 || dot(geometric_normal, n1) < 0.999 ||
					dot(geometric_normal, n2) < 0.999)
					is_soft_surface = 1;
			}

			int3 t_idx = tindex_buffer[ primIdx ];
			if ( texcoord_buffer.size() == 0 || t_idx.x < 0 || t_idx.y < 0 || t_idx.z < 0 ) {
				texcoord = make_float3( 0.0f, 0.0f, 0.0f );
			} else {
				float2 t0 = texcoord_buffer[ t_idx.x ];
				float2 t1 = texcoord_buffer[ t_idx.y ];
				float2 t2 = texcoord_buffer[ t_idx.z ];
				texcoord = make_float3( t1*beta + t2*gamma + t0*(1.0f-beta-gamma) );
			}
			triangle_area = length(cross(p0 - p1, p0 - p2)) / 2.0f;

			////Myron Here !!!!!!!!!!!!!!!!!!!!!!!!!!
			rtReportIntersection(material_buffer[primIdx]);
		}
	}
}

RT_PROGRAM void mesh_bounds (int primIdx, float result[6])
{  
	const int3 v_idx = vindex_buffer[primIdx];

	const float3 v0   = vertex_buffer[ v_idx.x ];
	const float3 v1   = vertex_buffer[ v_idx.y ];
	const float3 v2   = vertex_buffer[ v_idx.z ];
	const float  area = length(cross(v1-v0, v2-v0));

	optix::Aabb* aabb = (optix::Aabb*)result;

	if(area > 0.0f && !isinf(area)) {
		aabb->m_min = fminf( fminf( v0, v1), v2 );
		aabb->m_max = fmaxf( fmaxf( v0, v1), v2 );
	} else {
		aabb->invalidate();
	}
}

